package channelService

import (
	"go-fox-edge-channel-opc-ua/channelEntity"
	"context"
	"errors"
	"gitee.com/fierce_wolf/go-fox-edge-channel-common/channelLogger"
	"gitee.com/fierce_wolf/go-fox-edge-common/commRedisRpc"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Method"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Reflect"
	"github.com/gopcua/opcua"
	"github.com/gopcua/opcua/ua"
)

type executeService struct {
}

func (e *executeService) execute(clientEntity *channelEntity.OpcUaEntity, requestVO *commRedisRpc.ChannelRequestVO) (*commRedisRpc.ChannelRespondVO, error) {
	if requestVO.Send == nil || requestVO.Send == "" {
		return nil, errors.New("发送数据不能为空！")
	}
	if requestVO.Timeout == 0 {
		return nil, errors.New("超时参数不能为空！")
	}

	// 获得连接
	val, ok := opcUaConnector.channelName2Client.Load(requestVO.Name)
	if !ok {
		return nil, errors.New("与远端尚未建立连接:" + requestVO.Name)
	}
	client := val.(*opcua.Client)

	// 检查：参数是否合法
	if requestVO.Send == nil || !Reflect.IsMap(requestVO.Send) {
		return nil, errors.New("命令参数格式必须为JSOM格式:" + requestVO.Name)
	}

	send := requestVO.Send.(map[string]interface{})

	// 打印日志
	channelLogger.PrintChannelDebugLogger(requestVO.Name, "发送数据", requestVO.Send)

	// 执行操作
	recv, err := e.doMethod(client, send)
	if err != nil {
		return nil, err
	}

	respondVO := commRedisRpc.Type.ChannelRespondVO.Ok(requestVO)
	respondVO.Recv = recv
	return respondVO, nil
}

func (e *executeService) doMethod(client *opcua.Client, request map[string]interface{}) (interface{}, error) {
	method := Map.GetString(request, "method", "")
	data := Map.GetSlice(request, "data", make([]interface{}, 0))

	if Method.HasEmpty(method, data) {
		return nil, errors.New("参数格式缺失: method, params")
	}

	if method == "read" {
		data, err := e.readValue(client, data)
		if err != nil {
			return nil, err
		}
		result := make(map[string]interface{})
		result["method"] = method
		result["data"] = data

		return result, nil
	}
	if method == "write" {
		return e.writeValue(client, data)
	}

	return nil, nil
}

func (e *executeService) readValue(client *opcua.Client, ids []interface{}) ([]map[string]interface{}, error) {
	ctx := context.Background()

	// 对NodeId的列表列表
	idx2str := make(map[int]string)
	readValueIds := make([]*ua.ReadValueID, 0)
	for k, v := range ids {
		id := v.(string)

		idx2str[k] = id

		nodeID, err := ua.ParseNodeID(id)
		if err != nil {
			return nil, errors.New("nodeId格式不正确：" + id + " -> " + err.Error())
		}

		readValueID := &ua.ReadValueID{}
		readValueID.NodeID = nodeID

		readValueIds = append(readValueIds, readValueID)
	}

	// 创建一个读数据请求
	req := &ua.ReadRequest{
		MaxAge:             2000,
		NodesToRead:        readValueIds,
		TimestampsToReturn: ua.TimestampsToReturnBoth,
	}

	// 读取数据
	rsp, err := client.Read(ctx, req)
	if err != nil {
		return nil, err
	}

	list := make([]map[string]interface{}, 0)
	for k, v := range rsp.Results {
		id := idx2str[k]

		data := make(map[string]interface{})
		data["id"] = id
		data["value"] = v.Value.Value()

		data["error"] = ""
		data["success"] = v.Status == ua.StatusOK
		if v.Status != ua.StatusOK {
			data["error"] = ua.StatusCodes[v.Status].Name
		}

		list = append(list, data)

	}

	return list, nil
}

func (e *executeService) writeValue(client *opcua.Client, params []interface{}) ([]map[string]interface{}, error) {
	ctx := context.Background()

	// 对NodeId的列表列表
	idx2str := make(map[int]string)
	writeValues := make([]*ua.WriteValue, 0)
	for k, v := range params {
		data := v.(map[string]interface{})

		id := Map.GetString(data, "id", "")
		value := data["value"]
		if id == "" || value == nil {
			return nil, errors.New("参数缺失：id, value")
		}

		idx2str[k] = id

		nodeID, err := ua.ParseNodeID(id)
		if err != nil {
			return nil, errors.New("nodeId格式不正确：" + id + " -> " + err.Error())
		}

		val, err := ua.NewVariant(value)
		if err != nil {
			return nil, err
		}

		writeValue := &ua.WriteValue{}
		writeValue.NodeID = nodeID
		writeValue.AttributeID = ua.AttributeIDValue
		writeValue.Value = &ua.DataValue{
			EncodingMask: ua.DataValueValue,
			Value:        val,
		}

		writeValues = append(writeValues, writeValue)
	}

	// 创建一个写数据请求
	req := &ua.WriteRequest{
		NodesToWrite: writeValues,
	}

	// 读取数据
	rsp, err := client.Write(ctx, req)
	if err != nil {
		return nil, err
	}

	list := make([]map[string]interface{}, 0)
	for k, v := range rsp.Results {
		id := idx2str[k]

		data := make(map[string]interface{})
		data["id"] = id
		data["error"] = ""
		data["success"] = v == ua.StatusOK
		if v != ua.StatusOK {
			data["error"] = ua.StatusCodes[v].Name
		}

		list = append(list, data)

	}

	return list, nil
}
